package com.treeman.api.controller.user;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import com.treeman.api.ApiRequestError;
import com.treeman.api.ApiRequestError.ApiError;
import com.treeman.api.controller.ApiExceptionResolverController;
import com.treeman.api.json.user.NotificationJson;
import com.treeman.api.json.user.UserJson;
import com.treeman.api.security.exception.InvalidUsernameOrPasswordException;
import com.treeman.friend.Invitation;
import com.treeman.friend.exception.InvitationAlreadyExistsException;
import com.treeman.friend.exception.InvitationException;
import com.treeman.friend.exception.InvitationNotFoundException;
import com.treeman.user.User;
import com.treeman.user.exception.UserNotFoundException;
import com.treeman.user.localization.LocalizationManager;
import com.treeman.user.service.UserService;

@Controller
@RequestMapping(value = "/api/me")
@PreAuthorize("isAuthenticated()")
public class CurrentUserDataController extends ApiExceptionResolverController {

	private static Logger log = Logger
			.getLogger(CurrentUserDataController.class);

	@Autowired
	private UserService userService;

	@Autowired
	private MessageSource messageSource;

	@RequestMapping(value = "", method = RequestMethod.GET)
	public @ResponseBody
	UserJson getCurrentUser() throws UserNotFoundException {

		String username = SecurityContextHolder.getContext()
				.getAuthentication().getName();

		log.debug("Getting current user info: " + username);

		UserJson userJson = userService.getUserJsonByUsername(username);
		return userJson;
	}

	@RequestMapping(value = "/friends", method = RequestMethod.GET)
	public @ResponseBody
	List<UserJson> getFriends() {
		String username = SecurityContextHolder.getContext()
				.getAuthentication().getName();

		log.debug("Getting all friends of user: " + username);

		Set<User> friends = userService.getUserFriends(username);
		List<UserJson> friendsJson = new ArrayList<UserJson>(friends.size());
		for (User friend : friends) {
			UserJson friendJson = new UserJson(friend);
			friendsJson.add(friendJson);
		}
		return friendsJson;
	}

	@RequestMapping(value = "/friends/follow", method = RequestMethod.GET)
	public @ResponseBody
	List<UserJson> getFriendsToFollow() {
		String username = SecurityContextHolder.getContext()
				.getAuthentication().getName();

		log.debug("Getting friends to follow of user: " + username);

		Set<User> friends = userService.getUserFriends(username);
		List<UserJson> friendsJson = new ArrayList<UserJson>(friends.size());
		for (User friend : friends) {
			if (LocalizationManager.getInstance().getUserLocalization(
					friend.getUsername()) != null) {
				UserJson friendJson = new UserJson(friend);
				friendsJson.add(friendJson);
			}
		}
		return friendsJson;
	}

	@RequestMapping(value = "/friends", method = RequestMethod.POST)
	public @ResponseBody
	void inviteFriend(
			@RequestParam(value = "friend_username", required = true) String friendUsername,
			Locale locale) throws InvitationException {
		String username = SecurityContextHolder.getContext()
				.getAuthentication().getName();

		if (username.equals(friendUsername)) {
			String errorMessage = messageSource.getMessage(
					"user.action.invitefriend.error", null, locale);
			throw new InvitationException(ApiError.INVITATION_ALREADY_EXISTS,
					errorMessage);
		}

		log.debug("User " + username + " wants to add user " + friendUsername);

		try {
			userService.inviteFriend(username, friendUsername);
			log.debug("Zaproszenie zostało stworzone.");
			return;
		} catch (UserNotFoundException e) {
			String errorMessage = messageSource.getMessage(
					"user.action.invitefriend.error.friend",
					new String[] { friendUsername }, locale);
			throw new InvitationException(
					ApiError.INVITATION_FRIEND_NOT_EXISTS, errorMessage);
		} catch (InvitationAlreadyExistsException e1) {
			String errorMessage = messageSource.getMessage(
					"user.action.invitefriend.error.exists", new String[] {
							username, friendUsername }, locale);
			throw new InvitationException(ApiError.INVITATION_ALREADY_EXISTS,
					errorMessage);
		}
	}

	@RequestMapping(value = "/notification", method = RequestMethod.GET)
	public @ResponseBody
	NotificationJson getInvitationsAndNotifications() {
		String username = SecurityContextHolder.getContext()
				.getAuthentication().getName();

		log.debug("Pobieram powiadomienia i zaproszenia dla użytkownika: "
				+ username);
		Set<Invitation> invitations = userService
				.getRecievedInvitations(username);
		log.debug("Liczba zaproszeń: " + invitations.size());
		NotificationJson json = new NotificationJson(invitations);
		return json;
	}

	@RequestMapping(value = "/invitation", method = RequestMethod.POST)
	public @ResponseBody
	void acceptRejectInvitation(
			@RequestParam(value = "accept", required = true) Boolean accept,
			@RequestParam(value = "user", required = true) String requestingUser,
			Locale locale) throws InvitationException {

		String username = SecurityContextHolder.getContext()
				.getAuthentication().getName();

		if (accept) {
			log.debug("Użytkownik " + username
					+ " akceptuje zaproszenie od użytkownika " + requestingUser);
			try {
				userService.acceptInvitationFromUser(username, requestingUser);
			} catch (InvitationNotFoundException e) {
				String errorMessage = messageSource.getMessage(
						"user.action.inviteaccept.error", null, locale);
				throw new InvitationException(ApiError.INVITATION_NOT_EXISTS,
						errorMessage);
			}
		} else {
			log.debug("Użytkownik " + username
					+ " NIE akceptuje zaproszenia od użytkownika "
					+ requestingUser);
			try {
				userService.rejectInvitationFromUser(username, requestingUser);
			} catch (InvitationNotFoundException e) {
				String errorMessage = messageSource.getMessage(
						"user.action.inviteaccept.error", null, locale);
				throw new InvitationException(ApiError.INVITATION_NOT_EXISTS,
						errorMessage);
			}
		}
	}

	@RequestMapping(value = "/settings/password", method = RequestMethod.POST)
	public @ResponseBody
	void changePassword(
			@RequestParam(value = "oldPassword") String oldPassword,
			@RequestParam(value = "newPassword") String newPassword,
			HttpServletResponse response) throws IOException {
		log.debug("Changing user password: " + oldPassword + " -> "
				+ newPassword);

		String username = SecurityContextHolder.getContext()
				.getAuthentication().getName();
		try {
			userService.changeUserPassword(username, newPassword, oldPassword);
		} catch (InvalidUsernameOrPasswordException e) {
			log.warn("Changing password failed. Old password incorrect!");
			response.sendError(HttpServletResponse.SC_BAD_REQUEST);
		}
	}

	/* EXCEPTIONS */
	@ExceptionHandler(UserNotFoundException.class)
	@ResponseStatus(value = HttpStatus.NOT_FOUND)
	public @ResponseBody
	ApiRequestError handleUserNotFoundException(UserNotFoundException e) {
		log.warn("User not found: " + e.getLocalizedMessage());
		return new ApiRequestError(ApiError.NOT_FOUND, "User not found");
	}

	@ExceptionHandler(InvitationException.class)
	@ResponseStatus(value = HttpStatus.BAD_REQUEST)
	public @ResponseBody
	ApiRequestError handleInvitationException(InvitationException e) {
		log.warn("InvitationException handled: " + e.getLocalizedMessage());
		return new ApiRequestError(e.getError(), e.getMessage());
	}
}
